package demo;

import java.util.*;
import java.io.*;

import conceptualization.Parser;
import conceptualization.NonKeywords;
import java.math.*;

public class QuerySystem {

	static String dataPath = "agg.txt";
	static String logPath = "sample.txt";
	static String aggPath = "agg.txt";
	
	static Parser p;
	static ArrayList<String> logList = new ArrayList<String>();
	static HashMap<Integer, ArrayList<String>> aggMap = new HashMap<Integer, ArrayList<String>>();
	
	static HashSet<String> nonKeyword = new HashSet<String>();
	static boolean doFilter = false;
	
	public static void main(String[] args) {
		timeStart();
		load();
		query();
	}
	
	static void query() {
		try {
			String line = "";
			int length, max;
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			while(!line.equals("exit")) {
				System.out.print("query: ");
				line = br.readLine().toLowerCase();
				line = testFilter(line);
				String[] terms = line.split(" ");
				length = terms.length;
				max = length;
				while (max != 0) {
					for (int i = 0; i + max <= length; i++) {
						String test = rebuild(terms, i, i+max);
						if (p.containConcept(test)) {
							test += "\t";
							terms[i] = test;
							for (int j = i+max; j < length; j++) {
								terms[j-max+1] = terms[j];
							}
							length = length - max + 1;
						}
					}
					max--;
				}
				ArrayList<String> conList = new ArrayList<String>();
				ArrayList<String> keywordList = new ArrayList<String>();
				for (int i = 0; i < length; i++) {
					if (terms[i].indexOf("\t") > 0) {
						conList.add(terms[i].replaceAll("\\t", ""));
					} else if (!nonKeyword.contains(terms[i])) {
						keywordList.add(terms[i]);
					}
				}
				if (conList.size() != 0) {
					ArrayList<ArrayList<String>> result = new ArrayList<ArrayList<String>>();
					for (String s : conList) {
						ArrayList<String> t = aggMap.get(p.conceptIndex(s));
						if (t != null) result.add(t);
						else result.add(new ArrayList<String>());
					}
					while (result.size() != 1) {
						ArrayList<String> ar1 = result.remove(result.size()-1);
						ArrayList<String> ar2 = result.remove(result.size()-1);
						result.add(intercept(ar1, ar2));
					}
					showResult(result.get(0), keywordList);
				} else System.out.println("No concept matches! ");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	static void showResult(ArrayList<String> result, ArrayList<String> keywords) {
		ArrayList<Node> ans = new ArrayList<Node>();
		for (String s : result) {
			Node n = new Node(s);
			n.setQuery(logList.get(n.qIndex));
			ans.add(n);
		}
		Object[] list = ans.toArray();
		if (doFilter) { // filtering part, just add a field to sort result-list
			for (int i = 0; i < list.length; i++) {
				Node tmpNode = (Node) list[i];
				tmpNode.mDegree = scoreKeywords(tmpNode.query, keywords);
			}
		}
		Arrays.sort(list);
		for (int i = 0; i < list.length; i++) {
			String query = logList.get(((Node) list[i]).qIndex);
			if (i == 0 || ((Node) list[i]).qIndex != ((Node) list[i-1]).qIndex) 
				System.out.println(((Node)list[i]).info());
		}
	}
	
	static ArrayList<String> intercept(ArrayList<String> left, ArrayList<String> right) {
		ArrayList<String> shortList = (left.size() < right.size())? left: right;
		ArrayList<String> longList = (left.size() < right.size())? right: left;
		ArrayList<String> ans = new ArrayList<String>();
		if (shortList.size() == 0) return ans;
		HashSet<String> set = new HashSet<String>();
		for (String s : shortList) {
			set.add(s);
		}
		for (String s: longList) {
			if (set.contains(s)) {
				ans.add(s);
			}
		}
		return ans;
	}
	
	static int scoreKeywords(String query, ArrayList<String> keywords) {
		int score = 0; 
		for (int i = 0; i < keywords.size(); i++) {
			if (query.indexOf(keywords.get(i)) != -1) 
				score += 5;
		}
		return score;
	}
	
	static void load() {
		p = new Parser();
		p.readConcept();
		System.out.print("Finish loading concept data... - "); time();
		loadLog();
		System.out.print("Finish loading log data... - "); time();
		loadAgg();
		System.out.print("Finish loading aggregation data... - "); time();
//		loadNonKeywords();
	}

	
	static void loadLog() {
		try {
			FileInputStream fis = new FileInputStream(logPath);
			DataInputStream dis = new DataInputStream(fis);
			BufferedReader br = new BufferedReader(new InputStreamReader(dis));
			String line = br.readLine();
			logList.add("");
			int count = 1;
			while (line != null && !line.equals("")) {
				String[] tmp = line.split("\\t");
				logList.add(tmp[0]);
				if (count % 100000 == 0) {
					System.out.print(count + " s loaded: "); time();
				}
				line = br.readLine();
				count++;
			}
			br.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	static void loadAgg() {
		try {
			FileInputStream fis = new FileInputStream(aggPath);
			DataInputStream dis = new DataInputStream(fis);
			BufferedReader br = new BufferedReader(new InputStreamReader(dis));
			String line = br.readLine();
			int count = 1;
			while (line != null && !line.equals("")) {
				String[] tmp = line.split("\\t");
				int conIndex = Integer.parseInt(tmp[0]);
				int num = Integer.parseInt(tmp[1]);
				ArrayList<String> nodes = new ArrayList<String>();
				for (int i = 0; i < num; i++) {
					line = br.readLine();
					tmp = line.split("\\t");
					nodes.add((tmp[0] + "\t" + tmp[1]));
				}
				aggMap.put(conIndex, nodes);
				if (count % 100000 == 0) {
					System.out.print(count + " c loaded: "); time();
				}
				line = br.readLine();
				count++;
			}
			br.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	static void loadNonKeywords() {
		String[] temp = NonKeywords.nonKeywords;
		for (int i = 0; i < temp.length; i++) {
			nonKeyword.add(temp[i]);
		}
	}

	static String rebuild(String[] terms, int start, int end) {
		String ans = "";
		for (int i = start; i < end; i++) {
			if (i == start) ans += terms[i];
			else ans += " " + terms[i];
		}
		return ans;
	}
	
	static String testFilter(String line) {
		String str = line.substring(line.length()-2);
		if (str.equals("-f")) {
			doFilter = true;
			return line.substring(0, line.length()-2).trim();
		} else {
			doFilter = false;
			return line.trim();
		}
	}

	static long startTimestamp, lastTimestamp;
	public static void timeStart() {
		startTimestamp = System.currentTimeMillis();
		lastTimestamp = startTimestamp;
	}
	public static void time() {
		long now = System.currentTimeMillis();
		System.out.print((now - lastTimestamp)/1000 + "s since last time;  ");
		System.out.println((now - startTimestamp)/1000 + "s since begin;  ");
		lastTimestamp = now;
	}
}

class Node implements Comparable<Node> {
	/** qIndex: query index; times: query frequency */
	int qIndex, times;
	/** matched degree */
	int mDegree;
	String query;
	
	Node(String line) {
		String[] t = line.split("\\t");
		this.qIndex = Integer.parseInt(t[0]);
		this.times = Integer.parseInt(t[1]);
		this.mDegree = 0;
	}
	
	Node(int qIndex, int times) {
		this.qIndex = qIndex;
		this.times = times;
		this.mDegree = 0;
	}
	
	Node(String qIndex, String times) {
		this.qIndex = Integer.parseInt(qIndex);
		this.times = Integer.parseInt(times);
		this.mDegree = 0;
	}
	
	void setQuery(String query) {
		this.query = query;
	}
	
	String info() {
		return qIndex + "\t" + times + " " + mDegree + "\t" + query;
	}

	@Override
	public int compareTo(Node n) {
		return compareMDegree(n);
	}
	
	private int compareMDegree(Node n) {
		int ans = this.mDegree - n.mDegree;
		return (ans != 0)? ans: compareTimes(n);
	}
	
	private int compareTimes(Node n) {
		int ans = this.times - n.times;
		return (ans != 0)? ans: compareIndex(n);
	}
	
	private int compareIndex(Node n) {
		return this.qIndex - n.qIndex;
	}
}
